package com.zerdoor.util.Interceptor;

import com.zerdoor.filter.RequestFilter;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

/**
 * Created by xiaobo on 2014/11/6.
 */
@Intercepts({
        @Signature(
                type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
        , @Signature(
        type = Executor.class,
        method = "update",
        args = {MappedStatement.class, Object.class})
})
public class SInterceptor implements Interceptor {
    public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final List<ResultMapping> EMPTY_RESULTMAPPING = new ArrayList<ResultMapping>(0);
    private static final String BOUND_SQL = "sqlSource.boundSql.sql";

    @SuppressWarnings("unchecked")
    private void test(final Object[] args) {
        final MappedStatement ms = (MappedStatement) args[0];
        final Object parameter = args[1];
        String mapperSQL = ms.getBoundSql(parameter).getSql();
        System.out.println("sql11>>>" + mapperSQL);
    }

    /**
     * 动态语言国际化拦截器
     * @param invocation
     * @return
     * @throws Throwable
     */
    public Object intercept(Invocation invocation) throws Throwable {

        final Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameterObject = args[1];
        BoundSql boundSql = ms.getBoundSql(parameterObject);

        //创建一个新的MappedStatement
        MappedStatement qs = newMappedStatement(ms, new BoundSqlSqlSource(boundSql));
        //将参数中的MappedStatement替换为新的qs，防止并发异常
        args[0] = qs;
        MetaObject msObject = forObject(qs);
        String sql = (String) msObject.getValue(BOUND_SQL);

        String language = "zh_cn";

        HttpServletRequest request = RequestFilter.threadLocal.get();
        if(request != null){
            HttpSession session = request.getSession();
            language = String.valueOf(session.getAttribute("language"));
        }


        System.out.println("language:"+language);
        //zh_cn  zh

        String replacement = "";
        if(language==null||language.equals("")){
            replacement = "_cn";
        }else{
            if(language.equals("zh_cn")){//简体中文
                replacement = "_cn";
            }else if(language.equals("en")){//英文
                replacement = "_en";
            }else{//其他语言默认用中文
                replacement = "_cn";
            }
        }

        msObject.setValue(BOUND_SQL, sql.replaceAll("_cn", replacement));
        //恢复类型
        msObject.setValue("resultMaps", ms.getResultMaps());

        test(invocation.getArgs());
        return invocation.proceed();
    }

    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    public void setProperties(Properties arg0) {
    }

    /**
     * 反射对象，增加对低版本Mybatis的支持
     *
     * @param object
     * @return
     */
    public static MetaObject forObject(Object object) {
        return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
    }

    /**
     * 由于MappedStatement是一个全局共享的对象，因而需要复制一个对象来进行操作，防止并发访问导致错误
     *
     * @param ms
     * @param newSqlSource
     * @return
     */
    private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId() + "_PageHelper", newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
            StringBuilder keyProperties = new StringBuilder();
            for (String keyProperty : ms.getKeyProperties()) {
                keyProperties.append(keyProperty).append(",");
            }
            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
            builder.keyProperty(keyProperties.toString());
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        //由于resultMaps第一次需要返回int类型的结果，所以这里需要生成一个resultMap - 防止并发错误
        List<ResultMap> resultMaps = new ArrayList<ResultMap>();
        ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), ms.getId(), int.class, EMPTY_RESULTMAPPING).build();
        resultMaps.add(resultMap);
        builder.resultMaps(resultMaps);
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());

        return builder.build();
    }

    private class BoundSqlSqlSource implements SqlSource {
        BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}
